import { observe, Dep } from './observer.js';
import { createElement, render, patch } from './vdom.js';

class Component {
  constructor(options) {
    this.$data = options.data ? options.data() : {};
    this.$methods = options.methods || {};
    this.$template = options.template;
    this.$slots = options.slots || {};
    this.$scopedSlots = options.scopedSlots || {};

    // 将方法绑定到组件实例
    Object.keys(this.$methods).forEach(key => {
      this[key] = this.$methods[key].bind(this);
    });

    observe(this.$data);
    this.$lifecycle = {
      beforeCreate: options.beforeCreate || (() => {}),
      created: options.created || (() => {}),
      beforeMount: options.beforeMount || (() => {}),
      mounted: options.mounted || (() => {}),
      beforeUpdate: options.beforeUpdate || (() => {}),
      updated: options.updated || (() => {}),
      beforeDestroy: options.beforeDestroy || (() => {}),
      destroyed: options.destroyed || (() => {})
    };
    this.beforeCreate();
    this.created();
    this.mount();
    this.$computed = this.initComputed(options.computed);
    this.initWatch(options.watch);
  }

  created() {
    this.$lifecycle.created.call(this);
  }

  mount() {
    this.update();
    this.$el = render(this.$vnode);
    this.$lifecycle.mounted.call(this);
    return this.$el;
  }

  shouldUpdate(newData) {
    return JSON.stringify(this.$data) !== JSON.stringify(newData);
  }

  update() {
    const newData = this.$data;
    if (this.shouldUpdate(newData)) {
      const oldVnode = this.$vnode;
      this.$vnode = this.render();
      if (oldVnode) {
        patch(oldVnode, this.$vnode);
        this.$lifecycle.updated.call(this);
      }
    }
  }

  render() {
    return this.$template(this.$data, this.$methods, this.$slots, this.$scopedSlots);
  }

  initComputed(computed = {}) {
    const self = this;
    Object.keys(computed).forEach(key => {
      Object.defineProperty(self, key, {
        get: computed[key].bind(self),
        enumerable: true,
        configurable: true
      });
    });
  }

  initWatch(watch = {}) {
    Object.keys(watch).forEach(key => {
      this.$watch(key, watch[key]);
    });
  }

  $watch(expOrFn, cb) {
    const self = this;
    let getter;
    if (typeof expOrFn === 'function') {
      getter = expOrFn;
    } else {
      getter = () => self.$data[expOrFn];
    }
    let oldValue = getter.call(self);
    Dep.target = () => {
      const newValue = getter.call(self);
      if (newValue !== oldValue) {
        cb.call(self, newValue, oldValue);
        oldValue = newValue;
      }
    };
    getter.call(self);
    Dep.target = null;
  }

  beforeCreate() {
    this.$lifecycle.beforeCreate.call(this);
  }

  beforeMount() {
    this.$lifecycle.beforeMount.call(this);
  }

  beforeUpdate() {
    this.$lifecycle.beforeUpdate.call(this);
  }

  beforeDestroy() {
    this.$lifecycle.beforeDestroy.call(this);
  }

  destroy() {
    this.beforeDestroy();
    // 清理工作，比如移除事件监听器等
    this.$lifecycle.destroyed.call(this);
  }
}

export { Component };